﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="MediaStockServiceTests.cs" company="DST Nexdox">
//   Copyright (c) DST Nexdox. All rights reserved.
// </copyright>
// <summary>
//   Defines the MediaStockServiceTests type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace ServiceTests
{
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using DocProcessingRepository.Interfaces;
  using Entities;
  using Exceptions;
  using FluentAssertions;
  using Moq;
  using NUnit.Framework;
  using ServiceInterfaces;
  using Services;

  [Category("Unit")]
  [TestFixture]
  public class MediaStockServiceTests
  {
    private IMediaStockService mediaStockService;
    private Mock<IMediaStockRepository> mediaStockRepositoryMock;
    private Mock<IUserService> userServiceMock;
    private Mock<IMediaDefinitionAuditService> mediaDefinitionAuditServiceMock;

    private ApplicationUser currentUser;

    [SetUp]
    public void Setup()
    {
      this.mediaStockRepositoryMock = new Mock<IMediaStockRepository>();
      this.mediaStockRepositoryMock = new Mock<IMediaStockRepository>();
      this.userServiceMock = new Mock<IUserService>();
      this.mediaDefinitionAuditServiceMock = new Mock<IMediaDefinitionAuditService>();
      this.mediaStockService = new MediaStockService(
        this.mediaStockRepositoryMock.Object,
        this.userServiceMock.Object,
        this.mediaDefinitionAuditServiceMock.Object);

      this.currentUser = new ApplicationUser("Username") { PreferredEnvironment = "Test" };
    }

    [Test]
    public void GivenAMediaDefinitionId_WhenITryToGetAMediaStock_TheMediaStocksShouldBeReturned()
    {
      var mediaStock = new MediaStockEntity();

      this.mediaStockRepositoryMock.Setup(
        x => x.GetById(It.IsAny<Int32>()))
        .Returns(mediaStock);

      MediaStockEntity returnedMediaStock = this.mediaStockService.GetById(1);

      this.mediaStockRepositoryMock.Verify(x => x.GetById(1), Times.Once);

      ((Object)returnedMediaStock).Should().BeSameAs(mediaStock);
    }

    [Test]
    public void GivenAMediaDefinitionId_WhenITryToGetAMediaStock_AndDatabaseIsUnavailable_ThenADocProcessingExceptionIsReturned()
    {
      this.mediaStockRepositoryMock.Setup(
        x => x.GetById(It.IsAny<Int32>()))
        .Throws<DocProcessingException>();

      Action act = () => this.mediaStockService.GetById(1);

      act.ShouldThrow<DocProcessingException>();
    }

    [Test]
    public void GivenAMediaDefinitionId_WhenITryToGetMediaStocks_TheMediaStocksShouldBeReturned()
    {
      var mediaStocks = new List<MediaStockEntity>();

      this.mediaStockRepositoryMock.Setup(
        x => x.GetByMediaDefinition(It.IsAny<Int32>()))
        .Returns(mediaStocks);

      List<MediaStockEntity> returnedMediaStocks = this.mediaStockService.GetByMediaDefinition(1);

      this.mediaStockRepositoryMock.Verify(x => x.GetByMediaDefinition(It.IsAny<Int32>()), Times.Once);

      ((Object)returnedMediaStocks).Should().BeSameAs(mediaStocks);
    }

    [Test]
    public void GivenAMediaDefinitionId_WhenITryToGetMediaStocks_AndDatabaseIsUnavailable_ThenADocProcessingExceptionIsReturned()
    {
      this.mediaStockRepositoryMock.Setup(
        x => x.GetByMediaDefinition(It.IsAny<Int32>()))
        .Throws<DocProcessingException>();

      Action act = () => this.mediaStockService.GetByMediaDefinition(1);

      act.ShouldThrow<DocProcessingException>();
    }

    [Test]
    public void GivenValidData_WhenITryToUpdateTheStationNumbers_TheStationNumbersAreUpdated()
    {
      this.userServiceMock.Setup(u => u.GetApplicationUser()).Returns(this.currentUser);

      var updates = new List<StationNumberAssignment>
                      {
                        new StationNumberAssignment { MediaStockID = 1, StationNumber = 2, StockId = 3, Code = "code1", Description = "description1"},
                        new StationNumberAssignment { MediaStockID = 2, StationNumber = 1, StockId = 4, Code = "code2", Description = "description2"}
                      };

      this.mediaStockService.UpdateStationNumbers(5, updates);

      this.mediaStockRepositoryMock.Verify(x => x.UpdateStationNumber(1, 2), Times.Once);
      this.mediaStockRepositoryMock.Verify(x => x.UpdateStationNumber(2, 1), Times.Once);

      this.mediaDefinitionAuditServiceMock.Verify(
        x =>
        x.AddAudit(
          5,
          this.currentUser.Id,
          MediaDefinitionAudit.MediaDefinitionAuditType.MediaStockStationNumbersUpdated,
          It.IsAny<DateTime>(),
          "Media stock 3 [code1:'description1'] station number updated to 2\r\n"
          + "Media stock 4 [code2:'description2'] station number updated to 1"));
    }

    [Test]
    public void GivenNoAssignments_WhenITryToUpdateTheStationNumbers_TheStationNumbersAreNotUpdated()
    {
      var emptyAssignmentsList = new List<StationNumberAssignment>();

      this.mediaStockService.UpdateStationNumbers(5, emptyAssignmentsList);

      this.mediaStockRepositoryMock.Verify(x => x.UpdateStationNumber(It.IsAny<Int32>(), It.IsAny<Int32>()), Times.Never);

      this.mediaDefinitionAuditServiceMock.Verify(
        x =>
        x.AddAudit(
          It.IsAny<Int32>(),
          It.IsAny<String>(),
          It.IsAny<MediaDefinitionAudit.MediaDefinitionAuditType>(),
          It.IsAny<DateTime>(),
          It.IsAny<String>()),
        Times.Never);
    }

    [Test]
    public void GivenValidData_WhenITryToUpdateTheStationNumber_AndDatabaseIsUnavailable_ThenADocProcessingExceptionIsReturned()
    {
      var updates = new List<StationNumberAssignment>
                      {
                        new StationNumberAssignment { MediaStockID = 1, StationNumber = 2 },
                        new StationNumberAssignment { MediaStockID = 2, StationNumber = 1 }
                      };

      this.mediaStockRepositoryMock.Setup(
        x => x.UpdateStationNumber(It.IsAny<Int32>(), It.IsAny<Int32>()))
        .Throws<DocProcessingException>();

      Action act = () => this.mediaStockService.UpdateStationNumbers(1, updates);

      act.ShouldThrow<DocProcessingException>();
    }

    [Test]
    public void GivenAMediaStockId_WhenITryToRemoveIt_ItIsRemoved()
    {
      var mediaStockToRemove = new MediaStockEntity
      {
        MediaStockId = 5,
        MediaDefId = 1,
        StockId = 2,
        Code = "code",
        Description = "description"
      };

      this.userServiceMock.Setup(u => u.GetApplicationUser()).Returns(this.currentUser);

      this.mediaStockRepositoryMock.Setup(x => x.GetById(It.IsAny<Int32>()))
        .Returns(mediaStockToRemove);

      this.mediaStockService.RemoveMediaStock(1);

      this.mediaStockRepositoryMock.Verify(x => x.DeleteMediaStock(1));

      this.mediaDefinitionAuditServiceMock.Verify(
        x =>
        x.AddAudit(
          1,
          this.currentUser.Id,
          MediaDefinitionAudit.MediaDefinitionAuditType.MediaStockRemoved,
          It.IsAny<DateTime>(),
          "Media stock 2 [code:'description'] removed"));
    }

    [Test]
    public void GivenAMediaStockId_WhenITryToRemoveIt_AndDatabaseIsUnavailable_ThenADocProcessingExceptionIsReturned()
    {
      this.mediaStockRepositoryMock.Setup(
        x => x.DeleteMediaStock(It.IsAny<Int32>()))
        .Throws<DocProcessingException>();

      Action act = () => this.mediaStockService.RemoveMediaStock(1);

      act.ShouldThrow<DocProcessingException>();
    }

    [Test]
    public void GivenValidData_WhenITryToAddStock_ItIsAdded()
    {
      var mediaStockAdded = new MediaStockEntity
                                           {
                                             MediaStockId = 5,
                                             MediaDefId = 1,
                                             StockId = 2,
                                             Code = "code",
                                             Description = "description"
                                           };

      this.userServiceMock.Setup(u => u.GetApplicationUser()).Returns(this.currentUser);

      this.mediaStockRepositoryMock.Setup(x => x.AddMediaStock(It.IsAny<Int32>(), It.IsAny<Int32>(), It.IsAny<Int32>()))
        .Returns(mediaStockAdded);

      this.mediaStockService.AddMediaStock(2, 1, 7);

      this.mediaStockRepositoryMock.Verify(x => x.AddMediaStock(2, 1, 7));

      this.mediaDefinitionAuditServiceMock.Verify(
        x =>
        x.AddAudit(
          1,
          this.currentUser.Id,
          MediaDefinitionAudit.MediaDefinitionAuditType.MediaStockAdded,
          It.IsAny<DateTime>(),
          "Media stock 2 [code:'description'] added with station number 7"));
    }

    [Test]
    public void GivenValidData_WhenITryToAddStock_AndDatabaseIsUnavailable_ThenADocProcessingExceptionIsReturned()
    {
      this.mediaStockRepositoryMock.Setup(
        x => x.AddMediaStock(It.IsAny<Int32>(), It.IsAny<Int32>(), It.IsAny<Int32>()))
        .Throws<DocProcessingException>();

      Action act = () => this.mediaStockService.AddMediaStock(1, 1, 1);

      act.ShouldThrow<DocProcessingException>();
    }
  }
}
